
#include <BFont>
#include <BStyle>
#include "member_BTableWidget.h"

using namespace BWE;

TableHeader::TableHeader()
{
	orientation = Orientation_None;
	sortable = false;
	resizable = true;
	space = 1;
	hoverIndex = -1;
	handle = -1;
	pressed = false;
	draging = false;
	show_first = -1;
	show_count = -1;
	dirty = false;
}
TableHeader::~TableHeader()
{

}

void TableHeader::updateDirtySections(const BStyle* style)
{
	const BFont* font = style->font();

	int hintWidth = 100;
	int hintHeight = 23;
	if (font)
	{
		hintWidth = font->width();
		hintHeight = font->height();
	}
	for (int i = 0; i < sections.size(); i++)
	{
		HeaderSection& section = sections[i];
		if (section.dirty)
		{
			if (style)
				style->trans(section.text, section.tr_text);
			else
				section.tr_text.clear();

			section.textSize.set(100, 23);
			if (font)
			{
				if (section.tr_text.size())
					section.textSize = font->textSize(section.tr_text);
				else
					section.textSize = font->textSize(section.text);
			}

			section.sizeHint = section.textSize;
			section.sizeHint.width() += 10;
			section.sizeHint.height() += 5;
			if (section.policy != Policy_Fixed)
			{
				int hintSize = 0;
				if (orientation == Orientation_Horizontal)
					hintSize = section.sizeHint.width();
				if (orientation == Orientation_Vertical)
					hintSize = section.sizeHint.height();
				if (hintSize > 0)
				{
					bool expanding = section.policy & Policy_Expanding;
					if (!expanding && section.size > hintSize)
						section.size = hintSize;
					bool shrinking = section.policy & Policy_Shrinking;
					if (!shrinking && section.size < hintSize)
						section.size = hintSize;
					if (section.policy & Policy_Preferred && section.size != hintSize)
						section.size = hintSize;
				}
			}
			section.dirty = false;
		}
		if (hintWidth < section.sizeHint.width())
			hintWidth = section.sizeHint.width();
		if (hintHeight < section.sizeHint.height())
			hintHeight = section.sizeHint.height();
	}
	hintSize.set(hintWidth, hintHeight);
}
int TableHeader::sectionUnderPoint(const BPoint& point)
{
	for (int i = 0; i < sections.size(); i++)
	{
		if (sections[i].rect.contain(point))
			return i;
	}
	return -1;
}
int TableHeader::handleUnderPoint(const BPoint& point)
{
	for (int i = 0; i < sections.size(); i++)
	{
		BRect rect = handleRect(i);
		if (rect.contain(point))
			return i;
	}
	return -1;
}

BRect TableHeader::handleRect(int index)
{
	int handleSize = bMax(space, 5);
	BRect rect;
	if (orientation == Orientation_Horizontal)
	{
		rect = sections[index].rect;
		rect.left() = rect.right() - handleSize;
		rect.right() += handleSize;
	}
	if (orientation == Orientation_Vertical)
	{
		rect = sections[index].rect;
		rect.top() = rect.bottom() - handleSize;
		rect.bottom() += handleSize;
	}
	return rect;
}

bool TableHeader::drag(const BPoint& pos)
{
	if (orientation == Orientation_Horizontal)
	{
		int xchange = pos.x() - dragingPoint.x();
		if (xchange == 0)
			return false;
		sections[handle].size += xchange;
		dragingPoint = pos;
		dirty = true;
		return true;
	}
	if (orientation == Orientation_Vertical)
	{
		int ychange = pos.y() - dragingPoint.y();
		if (ychange == 0)
			return false;
		sections[handle].size += ychange;
		int next = handle + 1;
		dragingPoint = pos;
		dirty = true;
		return true;
	}
	return false;
}
void TableHeader::paint(BPainter& painter)
{
	for (int i = 0; i < sections.size(); i++)
	{
		HeaderSection& sec = sections[i];
		if (!sec.visible)
			continue;
		StyleRole colorRole = Color_Background;
		if (sec.sortMode)
			colorRole = Color_Background_Checked;
		if (hoverIndex == i)
		{
			if (pressed)
				colorRole = Color_Background_Pressed;
			else
				colorRole = Color_Background_Hovered;
		}
		if (painter.setColor(colorRole))
		{
			painter.fillRect(sec.rect);
		}
		if (sec.text.size() && painter.setColor(Color_Text))
		{
			if (sec.tr_text.size())
				painter.drawText(sec.textRect, sec.tr_text);
			else
				painter.drawText(sec.textRect, sec.text);
		}
		if (sec.sortMode)
		{
			if (painter.setColor(Color_Arrow))
			{
				painter.setLineWidth(3);
				painter.setLineSmooth(true);
				BPoint center = sec.sortRect.center();
				BPoint p0(sec.sortRect.left(), center.y());
				BPoint p1(sec.sortRect.right(), center.y());
				BPoint up(center.x(), sec.sortRect.top());
				BPoint down(center.x(), sec.sortRect.bottom());
				if (sec.sortMode > 0)
				{
					painter.fillTriangle(up, p0, p1);
				}
				else
				{
					painter.fillTriangle(down, p1, p0);
				}
			}
		}
	}
	if (painter.setColor(Color_Separator))
	{
		painter.setLineSmooth(true);
	}
	for (int i = 0; i < sections.size(); i++)
	{
		HeaderSection& sec = sections[i];
		if (sec.visible)
		{
			const BRect& rect = sec.rect;
			if (orientation == Orientation_Horizontal)
				painter.drawLine(rect.right(), rect.top(), rect.right(), rect.bottom());
			if (orientation == Orientation_Vertical)
				painter.drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom());
		}
	}

	if (resizable && handle >= 0)
	{
		BRect rect = handleRect(handle);
		if (orientation == Orientation_Horizontal)
		{
			rect.left() = rect.center().x() - 1;
			rect.right() = rect.center().x() + 1;
		}
		if (orientation == Orientation_Vertical)
		{
			rect.top() = rect.center().y() - 1;
			rect.bottom() = rect.center().y() + 1;
		}		
		if (painter.setColor(Color_Handle))
		{
			painter.fillRect(rect);
		}
	}
}

member_BTableWidget::member_BTableWidget(BTableWidget* tableWidget)
{
	boss = tableWidget;
	gridStyle = Grid_None;
	selectMultiple = false;
	ctrl = false;
	shift = false;
	hoverItem = 0;
	shiftItem = 0;
	dirty = false;
	columnHeader.orientation = Orientation_Horizontal;
	rowHeader.orientation = Orientation_Vertical;
	selectedTime = 0;
}
member_BTableWidget::~member_BTableWidget()
{

}

BTableItem* member_BTableWidget::itemAtPoint(const BPoint& pos)
{
	if (items.empty())
		return 0;

	const BRect& crect = boss->clientRect();
	if (!crect.contain(pos))
		return 0;

	int row = -1;
	for (int i = 0; i < rowHeader.sections.size(); i++)
	{
		HeaderSection& sec = rowHeader.sections[i];
		BRect rect = sec.rect;
		if (rect.top() < pos.y() && rect.bottom() > pos.y())
		{
			row = i;
			break;
		}
	}
	int column = -1;
	for (int i = 0; i < columnHeader.sections.size(); i++)
	{
		HeaderSection& sec = columnHeader.sections[i];
		BRect rect = sec.rect;
		if (rect.left() < pos.x() && rect.right() > pos.x())
		{
			column = i;
			break;
		}
	}
	if (row >= 0 && column >= 0)
	{
		return items[row][column];
	}
	return 0;
}

void member_BTableWidget::adjustHeader()
{
	const BStyle* style = boss->realStyle();
	columnHeader.updateDirtySections(style);
	rowHeader.updateDirtySections(style);

	if (boss->perch(Part_Top) != columnHeader.hintSize.height())
		boss->setPerch(Part_Top, columnHeader.hintSize.height());
	if (boss->perch(Part_Left) != rowHeader.hintSize.width())
		boss->setPerch(Part_Left, rowHeader.hintSize.width());

	BRect area = boss->area();
	if (columnHeader.dirty)
	{
		columnHeader.show_first = -1;
		columnHeader.show_count = 0;
		const BRect& rect = boss->rect(Part_Top);
		int x = area.left();
		for (int i = 0; i < columnHeader.sections.size(); i++)
		{
			HeaderSection& sec = columnHeader.sections[i];
			if (sec.rect.left() != x || sec.rect.width() != sec.size)
			{
				sec.rect.left() = x;
				sec.rect.right() = x + sec.size;
				sec.adjust();
			}
			if (sec.rect.top() != rect.top() || sec.rect.bottom() != rect.bottom())
			{
				sec.rect.top() = rect.top();
				sec.rect.bottom() = rect.bottom();
				sec.adjust();
			}
			if (columnHeader.show_first < 0 && sec.rect.right() > rect.left())
				columnHeader.show_first = i;
			if (columnHeader.show_first >= 0 && sec.rect.left() < rect.right())
				columnHeader.show_count++;
			x += sec.size + columnHeader.space;
		}
		columnHeader.dirty = false;
	}
	if (rowHeader.dirty)
	{
		rowHeader.show_first = -1;
		rowHeader.show_count = 0;
		const BRect& rect = boss->rect(Part_Left);
		int y = area.top();
		for (int i = 0; i < rowHeader.sections.size(); i++)
		{
			HeaderSection& sec = rowHeader.sections[i];
			if (sec.rect.top() != y || sec.rect.height() != sec.size)
			{
				sec.rect.top() = y;
				sec.rect.bottom() = y + sec.size;
				sec.adjust();
			}
			if (sec.rect.left() != rect.left() || sec.rect.right() != rect.right())
			{
				sec.rect.left() = rect.left();
				sec.rect.right() = rect.right();
				sec.adjust();
			}
			if (rowHeader.show_first < 0 && sec.rect.bottom() > rect.top())
				rowHeader.show_first = i;
			if (rowHeader.show_first >= 0 && sec.rect.top() < rect.bottom())
				rowHeader.show_count++;
			y += sec.rect.height() + rowHeader.space;
		}
		rowHeader.dirty = false;
	}
}
void member_BTableWidget::adjustItemRects()
{
	const BFont* font = boss->font();
	if (items.empty())
		return;
	int row_first = rowHeader.show_first;
	int row_last = rowHeader.show_first + rowHeader.show_count;
	int col_first = columnHeader.show_first;
	int col_last = columnHeader.show_first + columnHeader.show_count;
	for (int r = row_first; r < row_last; r++)
	{
		int y = rowHeader.sections[r].rect.y();
		int h = rowHeader.sections[r].rect.height();
		for (int c = col_first; c < col_last; c++)
		{
			BTableItem* item = items[r][c];
			if (item)
			{
				if (item_member(item)->dirty)
				{
					item_member(item)->fresh(font);
					item_member(item)->dirty = false;
				}
				int x = columnHeader.sections[c].rect.x();
				int w = columnHeader.sections[c].rect.width();
				if (w > 0 && h > 0)
				{
					item_member(item)->adjust(x, y, w, h);
				}
			}
		}
	}
}
void member_BTableWidget::freshColumnRects(int column)
{
	if (items.empty())
		return;
	const BRect& rect = columnHeader.sections[column].rect;
	int x = rect.x();
	int w = rect.width();
	for (int r = 0; r < items.row(); r++)
	{
		BTableItem* item = items[r][column];
		if (item)
		{
			int y = rowHeader.sections[r].rect.y();
			int h = rowHeader.sections[r].rect.width();
			item_member(item)->adjust(x, y, w, h);
		}
	}
}
bool member_BTableWidget::sortColumn(int column)
{
	char mode = columnHeader.sections[column].sortMode;
	if (mode == 0)
		return false;
	for (int begin = 0; begin < items.row(); begin++)
	{
		int select = begin;
		BTableItem* selectItem = items[begin][column];
		for (int r = begin; r < items.row(); r++)
		{
			BTableItem* item = items[r][column];
			if (mode < 0 && selectItem->text() > item->text())
			{
				selectItem = item;
				select = r;
			}
			if (mode > 0 && selectItem->text() < item->text())
			{
				selectItem = item;
				select = r;
			}
		}
		if (select != begin)
		{
			BTableItemHolder holder;
			BTableItemHolder* beginItems = items[begin];
			BTableItemHolder* selectItems = items[select];
			for (int c = 0; c < items.column(); c++)
			{
				holder = beginItems[c];
				beginItems[c] = selectItems[c];
				selectItems[c] = holder;
			}
		}
	}
	dirty = true;
	return true;
}
